package message.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.function.Function;
import java.util.function.Supplier;

import message.GenericMessageBuilderSupplier;
import message.GenericMessageParserSupplier;
import message.Message;
import message.MessageBuilder;
import message.MessageBuilderSupplier;
import message.MessageParser;
import message.MessageParserSupplier;
import message.StructMessageBuilderSupplier;
import message.StructMessageParserSupplier;
import message.exception.UnknownTypeException;
import message.util.BiHashMap;
import message.util.MatchMap;

public class HandlerMapper {
	
	public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

	public MessageBuilderSupplier OBJECT_BUILDER_SUPPLIER;
	public MessageParserSupplier OBJECT_PARSER_SUPPLIER;
	
	private BiHashMap<String, MessageBuilderSupplier, MessageParserSupplier> map;
	private MatchMap<Long, String> typeMap;
	private Charset charset;
	
	public HandlerMapper() {
		map = new BiHashMap<>();
		typeMap = new MatchMap<>();
	}
	
//	public long register(long code, String type) {
//		typeMap.match(code, type);
////		getByType(type);
//		return code;
//	}
	public long register(long code, Type type) {
		typeMap.match(code, type.getTypeName());
//		getByType(type);
		return code;
	}
	
	public String getType(long code) {
		var x = typeMap.getLater(code);
		if(x == null) throw new UnknownTypeException();
		return x;
	}
	
	public long getTypeCode(Type type){
		var x = typeMap.getFormer(type.getTypeName());
		if(x == null) throw new UnknownTypeException();
		return x;
	}
	
	public MessageBuilderSupplier getBuilderSupplier(Type c){
		return getByType(c).getFormer();
	}
	public MessageParserSupplier getParserSupplier(Type c){
		return getByType(c).getLater();
	}
	public HandlerMapper add(MessageBuilderSupplier builder, MessageParserSupplier parser, String...c) {
		for(String x: c) {
			map.put(x, builder, parser);
		}
		return this;
	}
//	public void registerType(String name, MessageBuilderSupplier builderSupplier, MessageParserSupplier parserSupplier) {
//		map.put(name, builderSupplier, parserSupplier);
//	}
//	public BiHashMap.Pair<MessageBuilderSupplier, MessageParserSupplier> getByName(String name) {
//		return map.get(name);
//	}
	public BiHashMap.Pair<MessageBuilderSupplier, MessageParserSupplier> getByType(Type type){
		String key = type.getTypeName();
		var x = map.get(key); // 缓存中检索
		if(x == null) { // 缓存未找到
			if(type instanceof ParameterizedType generic) { // 泛型类型
				String raw = generic.getRawType().getTypeName();
				var rawb = map.get(raw);
				if(rawb != null) {
					if(rawb.getFormer() instanceof GenericMessageBuilderSupplier supplier 
					&& rawb.getLater() instanceof GenericMessageParserSupplier parser) {
						Type[] types = generic.getActualTypeArguments();
						int length = types.length;
						MessageBuilderSupplier[] builders = new MessageBuilderSupplier[length];
						MessageParserSupplier[] parsers = new MessageParserSupplier[length];
						for(int i = 0; i<length; i++) {
							var t = getByType(types[i]);
							builders[i] = t.getFormer();
							parsers[i] = t.getLater();
						}
						x = BiHashMap.wrapPair(supplier.wrapGeneric(builders), parser.wrapGeneric(parsers));
					}
				}
				else try {
					throw new Exception();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			else { // 非泛型类型
				Class<?> c = null;
				try {
					c = Class.forName(key);
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				}
				x = reflect(c);
			}
			map.put(key, x);
		}
		return x;
	}
	
	private BiHashMap.Pair<MessageBuilderSupplier, MessageParserSupplier> 
	reflect(Class<?> c){
		ArrayList<Function<? super Object, ? extends MessageBuilder>> subBuilders = new ArrayList<>();
		ArrayList<Function<? super Object, ? extends MessageParser>> subParsers = new ArrayList<>();
		if(!Message.class.isAssignableFrom(c)) {
			try {
				throw new Exception(); // 未实现Message接口
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		Supplier<? extends Object> constructor = () -> { // 无参构造函数
				try {
					Constructor<?> cons = c.getConstructor(new Class<?>[0]);
					cons.setAccessible(true);
					return cons.newInstance(new Object[0]);
				} catch (InstantiationException |
						IllegalAccessException |
						IllegalArgumentException |
						InvocationTargetException |
						NoSuchMethodException |
						SecurityException e) {
					e.printStackTrace();
				}
			return null;
		};
		Field[] fields = c.getDeclaredFields();
		for(Field x: fields) {
			// 忽略static和transient属性
			if(Modifier.isStatic(x.getModifiers()) 
					|| Modifier.isTransient(x.getModifiers())) continue;
			x.setAccessible(true);
			// 获取属性类型
			var pair = this.getByType(x.getGenericType());
			MessageBuilderSupplier subBuilder = pair.getFormer();
			MessageParserSupplier subParser = pair.getLater();
			subBuilders.add((obj)->subBuilder.get((build)->{
				try {
					x.set(obj, build);
				} catch (IllegalArgumentException | IllegalAccessException e) {
					e.printStackTrace();
				}
			}));
			subParsers.add((obj)->{
				try {
					return subParser.parse(x.get(obj));
				} catch (IllegalArgumentException | IllegalAccessException e) {
					e.printStackTrace();
				}
				return null;
			});
		}
		return BiHashMap.wrapPair(new StructMessageBuilderSupplier(subBuilders, constructor), new StructMessageParserSupplier(subParsers));
	}
	
	public void setCharset(Charset charset) {
		this.charset = charset;
	}
	
	public Charset getCharset() {
		if(this.charset == null) return DEFAULT_CHARSET;
		return this.charset;
	}
	
}
